#include "InputHandler.h"
#include <iostream>

void globalMouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
	InputHandler::globalInputHandler->mouseButtonCallback(button, action, mods);
}

void globalKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
	InputHandler::globalInputHandler->keyCallback(key, scancode, action, mods);
}

void globalScrollCallback(GLFWwindow* window, double x, double y)
{
	InputHandler::globalInputHandler->scrollCallback(x,y);
}

void globalCharacterCallback(GLFWwindow* window, unsigned int character)
{
	InputHandler::globalInputHandler->characterCallback(character);
}

void globalFramebufferResizeCallback(GLFWwindow* window, int width, int height)
{
	InputHandler::globalInputHandler->framebufferResizeCallback(width, height);
}

void globalWindowResizeCallback(GLFWwindow* window, int width, int height)
{
	InputHandler::globalInputHandler->windowResizeCallback(width, height);
}

void InputHandler::framebufferResizeCallback(int width, int height)
{
	for (auto listener : _windowEventListeners)
	{
		listener->framebufferResized(width, height);
	}
}

void InputHandler::windowResizeCallback(int width, int height)
{
	for (auto listener : _windowEventListeners)
	{
		listener->windowResized(width, height);
	}
}

void InputHandler::scrollCallback(double x, double y)
{
	_wheelChange += y;
	for (auto listener : _mouseEventListeners)
	{
		listener->wheelChanged(x,y);
	}
}

void InputHandler::characterCallback(unsigned int character)
{
	for(auto listener : _characterEventListeners)
	{
		listener->characterCallback(character);
	}
}

InputHandler::InputHandler(GLFWwindow* window)
	: _window(window), _wheelChange(0.), _mouseWheelSpeed(0)
{
	globalInputHandler = this;
	for (int i=0; i < GLFW_MOUSE_BUTTON_LAST; i++)
	{
		_buttonDown[i] = false;
		_buttonPressed[i] = 0;
		_buttonReleased[i] = 0;
	}
	for (int i=0; i < 349; i++)
	{
		_keyDown[i] = false;
		_keyPressed[i] = 0;
		_keyReleased[i] = 0;
	}
	glfwSetMouseButtonCallback(_window, globalMouseButtonCallback);
	glfwSetKeyCallback(_window, globalKeyCallback);
	glfwSetCharCallback(_window, globalCharacterCallback);
	glfwSetScrollCallback(_window, globalScrollCallback);
	glfwSetFramebufferSizeCallback(_window, globalFramebufferResizeCallback);
	glfwSetWindowSizeCallback(_window, globalWindowResizeCallback);
}

void InputHandler::addListener(CharacterEventListener* listener)
{
	_characterEventListeners.insert(listener);
}
void InputHandler::addListener(KeyEventListener* listener)
{
	_keyEventListeners.insert(listener);
}
void InputHandler::addListener(MouseEventListener* listener)
{
	_mouseEventListeners.insert(listener);
}
void InputHandler::addListener(WindowEventListener* listener)
{
	_windowEventListeners.insert(listener);
}

void InputHandler::removeListener(CharacterEventListener* listener)
{
	_characterEventListeners.erase(listener);
}
void InputHandler::removeListener(KeyEventListener* listener)
{
	_keyEventListeners.erase(listener);
}
void InputHandler::removeListener(MouseEventListener* listener)
{
	_mouseEventListeners.erase(listener);
}
void InputHandler::removeListener(WindowEventListener* listener)
{
	_windowEventListeners.erase(listener);
}

void InputHandler::mouseButtonCallback(int button, int action, int mods)
{
	if (action == GLFW_PRESS)
	{
		_buttonPressed[button] = 1;
		for(auto listener : _mouseEventListeners)
		{
			listener->buttonPressed(button,mods);
		}
	}
	else if (action == GLFW_RELEASE)
	{
		_buttonReleased[button] = 1;
		for(auto listener : _mouseEventListeners)
		{
			listener->buttonReleased(button,mods);
		}
	}
}

void InputHandler::keyCallback(int key, int scancode, int action, int mods)
{
	if (action == GLFW_PRESS)
	{
		_keyPressed[key] = 1;
		for(auto listener : _keyEventListeners)
		{
			listener->keyPressed(key,scancode,mods);
		}
	}
	else if (action == GLFW_RELEASE)
	{
		_keyReleased[key] = 1;
		for(auto listener : _keyEventListeners)
		{
			listener->keyReleased(key,scancode,mods);
		}
	}
}

bool InputHandler::isMouseDown(int button) const
{
	return _buttonDown[button];
}

bool InputHandler::isMousePressed(int button) const
{
	return _buttonPressed[button] == 2;
}

bool InputHandler::isMouseReleased(int button) const
{
	return _buttonReleased[button] == 2;
}

bool InputHandler::isKeyDown(int key) const
{
	return _keyDown[key];
}

bool InputHandler::isKeyPressed(int key) const
{
	return _keyPressed[key] == 2;
}

bool InputHandler::isKeyReleased(int key) const
{
	return _keyReleased[key] == 2;
}

int InputHandler::getMouseWheelSpeed() const
{
	return _mouseWheelSpeed;
}

void InputHandler::update()
{
	_mouseWheelSpeed = (int)_wheelChange;
	_wheelChange = 0.;

	for (int i=0; i < GLFW_MOUSE_BUTTON_LAST; i++)
	{
		if (_buttonPressed[i] > 0)
			_buttonPressed[i] = (_buttonPressed[i]+1)%3;
		if (_buttonReleased[i] > 0)
			_buttonReleased[i] = (_buttonReleased[i]+1)%3;

		if (glfwGetMouseButton(_window,i))
		{
			_buttonDown[i] = true;
		}
		else
		{
			_buttonDown[i] = false;
		}
	}

	for (int i=0; i < 349; i++)
	{
		if (_keyPressed[i] > 0)
			_keyPressed[i] = (_keyPressed[i]+1)%3;
		if (_keyReleased[i] > 0)
			_keyReleased[i] = (_keyReleased[i]+1)%3;

		if (glfwGetKey(_window, i))
		{
			_keyDown[i] = true;
		}
		else
		{
			_keyDown[i] = false;
		}
	}
}

InputHandler* InputHandler::globalInputHandler = nullptr;